#include "taskjoint.hh"
#include <stdio.h>
#include <map>
#include <ctime>
#include <cmath>
#include <string>

#include "errcode.h"
#include "jkutil/iniparser.h"
#include "string.h"
// #include "zucIniFile.hh"
// #include "minIni.h"

#define _PRINT(fmt, ...)       //printf(fmt, ##__VA_ARGS__)
#define THRESHOLD_MAX 1048576  //2^20
#define THRESHOLD_MIN 20

class ServoStatistic;
static ServoStatistic* _i = nullptr;

ServoStatistic* ServoStatistic::get()
{
    if (_i == nullptr)
    {
        _i = new ServoStatistic();
    }
    return _i;
}
ServoStatistic::ServoStatistic() { init(); }
ServoStatistic::~ServoStatistic()
{
    if (_i == nullptr)
        return;
    delete _i;
    _i = nullptr;
}
int ServoStatistic::init(uint32_t serialNum)
{
    _serialNum = serialNum;
    _date = 0;
    _reportedDaliyWarn = 0;
    for (int i = 0; i < JOINT_NUM; i++)
    {
        _warnThresHold[i] = 0;
        ServoStatisticChn chn;
        memset(&chn, 0, sizeof(chn));
        _servoStatistic[i].insert(std::pair<uint32_t, ServoStatisticChn>(DRIVE_WARN_ENCODE_CNT, chn));
        _servoStatistic[i].insert(std::pair<uint32_t, ServoStatisticChn>(DRIVE_WARN_ENCODE_DUAL_CHECK, chn));
        _servoStatistic[i].insert(std::pair<uint32_t, ServoStatisticChn>(DRIVE_WARN_ENCODE_Z, chn));
        _servoStatistic[i].insert(std::pair<uint32_t, ServoStatisticChn>(DRIVE_WARN_ENCODE_SIGNAL, chn));
        _servoStatistic[i].insert(std::pair<uint32_t, ServoStatisticChn>(DRIVE_WARN_ENCODE_INNER, chn));
    }
    return 0;
}
void ServoStatistic::load(void* file)
{
    //ZucIniFile* trajInifile = (ZucIniFile*)file;
    Util::IniParser* ini_file = (Util::IniParser*)file;

    //int threshold[JOINT_NUM] = {100};
    //trajInifile->FindArray(threshold, JOINT_NUM, "WARN_THRSEHOLD", "SERVOSTATISTIC");
    std::vector<int> threshold(JOINT_NUM, 100);
    std::vector<int> default_threshold(JOINT_NUM, 100);
    ini_file->get("SERVOSTATISTIC.WARN_THRSEHOLD", threshold, default_threshold, THRESHOLD_MIN, THRESHOLD_MAX);
    for (int i = 0; i < JOINT_NUM; i++) { _warnThresHold[i] = threshold[i]; }
    int date = 0;
    ini_file->get("SERVOSTATISTIC.STATISTIC_DATE", date, 0, 0, INT32_MAX);
    //trajInifile->Find(&date, "STATISTIC_DATE", "SERVOSTATISTIC");
    _date = date;

    // int cnt[JOINT_NUM] = {0}, daliyCnt[JOINT_NUM] = {0}, lastCnt[JOINT_NUM];
    // trajInifile->FindArray(cnt, JOINT_NUM, "WARN_8384_ALL_CNT", "SERVOSTATISTIC");
    // trajInifile->FindArray(daliyCnt, JOINT_NUM, "WARN_8384_DALIY_CNT", "SERVOSTATISTIC");
    std::vector<int> daliyCnt(JOINT_NUM, 0);
    std::vector<int> cnt(JOINT_NUM, 0);
    std::vector<int> lastCnt(JOINT_NUM, 0);
    std::vector<int> defaultCnt(JOINT_NUM, 0);
    ini_file->get("SERVOSTATISTIC.WARN_8384_ALL_CNT", cnt, defaultCnt, 0, INT32_MAX);
    ini_file->get("SERVOSTATISTIC.WARN_8384_DALIY_CNT", daliyCnt, defaultCnt, 0, INT32_MAX);
    for (int i = 0; i < JOINT_NUM; i++) { lastCnt[i] = cnt[i] - daliyCnt[i]; }
    setStatistic(DRIVE_WARN_ENCODE_CNT, cnt, lastCnt);

    // trajInifile->FindArray(cnt, JOINT_NUM, "WARN_8385_ALL_CNT", "SERVOSTATISTIC");
    // trajInifile->FindArray(daliyCnt, JOINT_NUM, "WARN_8385_DALIY_CNT", "SERVOSTATISTIC");
    ini_file->get("SERVOSTATISTIC.WARN_8385_ALL_CNT", cnt, defaultCnt, 0, INT32_MAX);
    ini_file->get("SERVOSTATISTIC.WARN_8385_DALIY_CNT", daliyCnt, defaultCnt, 0, INT32_MAX);
    for (int i = 0; i < JOINT_NUM; i++) { lastCnt[i] = cnt[i] - daliyCnt[i]; }
    setStatistic(DRIVE_WARN_ENCODE_DUAL_CHECK, cnt, lastCnt);

    // trajInifile->FindArray(cnt, JOINT_NUM, "WARN_8387_ALL_CNT", "SERVOSTATISTIC");
    // trajInifile->FindArray(daliyCnt, JOINT_NUM, "WARN_8387_DALIY_CNT", "SERVOSTATISTIC");
    ini_file->get("SERVOSTATISTIC.WARN_8387_ALL_CNT", cnt, defaultCnt, 0, INT32_MAX);
    ini_file->get("SERVOSTATISTIC.WARN_8387_DALIY_CNT", daliyCnt, defaultCnt, 0, INT32_MAX);
    for (int i = 0; i < JOINT_NUM; i++) { lastCnt[i] = cnt[i] - daliyCnt[i]; }
    setStatistic(DRIVE_WARN_ENCODE_Z, cnt, lastCnt);

    // trajInifile->FindArray(cnt, JOINT_NUM, "WARN_8388_ALL_CNT", "SERVOSTATISTIC");
    // trajInifile->FindArray(daliyCnt, JOINT_NUM, "WARN_8388_DALIY_CNT", "SERVOSTATISTIC");
    ini_file->get("SERVOSTATISTIC.WARN_8388_ALL_CNT", cnt, defaultCnt, 0, INT32_MAX);
    ini_file->get("SERVOSTATISTIC.WARN_8388_DALIY_CNT", daliyCnt, defaultCnt, 0, INT32_MAX);
    for (int i = 0; i < JOINT_NUM; i++) { lastCnt[i] = cnt[i] - daliyCnt[i]; }
    setStatistic(DRIVE_WARN_ENCODE_SIGNAL, cnt, lastCnt);

    // trajInifile->FindArray(cnt, JOINT_NUM, "WARN_7001_ALL_CNT", "SERVOSTATISTIC");
    // trajInifile->FindArray(daliyCnt, JOINT_NUM, "WARN_7001_DALIY_CNT", "SERVOSTATISTIC");
    ini_file->get("SERVOSTATISTIC.WARN_7001_ALL_CNT", cnt, defaultCnt, 0, INT32_MAX);
    ini_file->get("SERVOSTATISTIC.WARN_7001_DALIY_CNT", daliyCnt, defaultCnt, 0, INT32_MAX);
    for (int i = 0; i < JOINT_NUM; i++) { lastCnt[i] = cnt[i] - daliyCnt[i]; }
    setStatistic(DRIVE_WARN_ENCODE_INNER, cnt, lastCnt);
}

int ServoStatistic::reset(uint32_t serialNum)
{
    bool isNewRobot = false;
    if (serialNum != _serialNum)
        isNewRobot = true;

    if (isNewRobot)
    {
        printServoStatistic();
        _serialNum = serialNum;
    }
    for (int i = 0; i < JOINT_NUM; i++)
    {
        for (auto e = _servoStatistic[i].begin(); e != _servoStatistic[i].end(); e++)
        {
            if (isNewRobot)  // 对于换机器人的情况需要全部复位
            {
                e->second.cntBeforePowerOn = 0;
                e->second.cntLastDay = 0;
            }
            e->second.cntWhenPowerOn = 0;
        }
    }
    updateDaliy();
    return isNewRobot;
}
void ServoStatistic::formatStatistic(uint32_t ecode, std::string& all, std::string& daliy)
{
    all = "{";
    all += std::to_string(getAllCnt(0, ecode)) + ",";
    all += std::to_string(getAllCnt(1, ecode)) + ",";
    all += std::to_string(getAllCnt(2, ecode)) + ",";
    all += std::to_string(getAllCnt(3, ecode)) + ",";
    all += std::to_string(getAllCnt(4, ecode)) + ",";
    all += std::to_string(getAllCnt(5, ecode)) + "}";
    daliy = "{";
    daliy += std::to_string(getDaliyCnt(0, ecode)) + ",";
    daliy += std::to_string(getDaliyCnt(1, ecode)) + ",";
    daliy += std::to_string(getDaliyCnt(2, ecode)) + ",";
    daliy += std::to_string(getDaliyCnt(3, ecode)) + ",";
    daliy += std::to_string(getDaliyCnt(4, ecode)) + ",";
    daliy += std::to_string(getDaliyCnt(5, ecode)) + "}";
}
void ServoStatistic::write2File(void* file)
{
    printServoStatistic();
    for (int i = 0; i < JOINT_NUM; i++)
    {
        for (auto e = _servoStatistic[i].begin(); e != _servoStatistic[i].end(); e++)
        {
            e->second.cntBeforePowerOn = getAllCnt(i, e->first);
            e->second.cntWhenPowerOn = 0;
        }
    }

    char* filename = (char*)file;
    const char* section = "SERVOSTATISTIC";
    // ini_putl(section, "STATISTIC_DATE", getDate(), filename);
    std::string nodename = "SERVOSTATISTIC.STATISTIC_DATE";
    Util::IniParser ini_file(filename);
    ini_file.set(nodename, (int32_t)getDate());  //6位数，不会超范围

    std::string all, daliy;
#define _INI_SET(ECODE)                                                       \
    formatStatistic(0x##ECODE, all, daliy);                                   \
    ini_file.set(std::string("SERVOSTATISTIC.WARN_" #ECODE "_ALL_CNT"), all); \
    ini_file.set(std::string("SERVOSTATISTIC.WARN_" #ECODE "_DALIY_CNT"), daliy)
    _INI_SET(8384);
    _INI_SET(8385);
    _INI_SET(8387);
    _INI_SET(8388);
    _INI_SET(7001);
#undef _INI_SET
}
void ServoStatistic::updateDaliy()
{
    auto newDay = isNewDay(_date);
    if (newDay)
    {
        for (int i = 0; i < JOINT_NUM; i++)
        {
            for (auto e = _servoStatistic[i].begin(); e != _servoStatistic[i].end(); e++) { e->second.cntLastDay = getAllCnt(i, e->first); }
        }
        _reportedDaliyWarn = 0;
        _date = newDay;
        printServoStatistic();
    }
}
int ServoStatistic::update(uint8_t joint, uint32_t ecode, uint32_t cnt)
{
    if (joint >= JOINT_NUM)
        return -1;
    if (_servoStatistic[joint].count(ecode) == 0)
        return -2;
    if (_servoStatistic[joint][ecode].cntWhenPowerOn != cnt)
    {
        _servoStatistic[joint][ecode].cntWhenPowerOn = cnt;
    }
    return 0;
}

uint32_t ServoStatistic::getAllCnt(uint8_t joint, uint32_t ecode)
{
    if (joint >= JOINT_NUM)
        return 0;
    if (_servoStatistic[joint].count(ecode) == 0)
        return 0;
    auto stat = _servoStatistic[joint][ecode];
    return stat.cntBeforePowerOn + stat.cntWhenPowerOn;
}
uint32_t ServoStatistic::getDaliyCnt(uint8_t joint, uint32_t ecode)
{
    if (joint >= JOINT_NUM)
        return 0;
    if (_servoStatistic[joint].count(ecode) == 0)
        return 0;
    auto stat = _servoStatistic[joint][ecode];
    return stat.cntBeforePowerOn + stat.cntWhenPowerOn - stat.cntLastDay;
}

uint32_t ServoStatistic::isNewDay(uint32_t d)
{
    time_t cur = time(0);
    auto curtm = gmtime(&cur);
    uint32_t curDay = curtm->tm_year % 100 * 10000 + (curtm->tm_mon + 1) % 100 * 100 + curtm->tm_mday;
    if (curDay != d)
        return curDay;
    return 0;
}
void ServoStatistic::setStatistic(uint32_t errcode, std::vector<int>& cnt, std::vector<int>& cntlast)
{
    for (int i = 0; i < JOINT_NUM; i++)
    {
        if (_servoStatistic[i].count(errcode) != 0)
        {
            _servoStatistic[i][errcode].cntBeforePowerOn = cnt[i];
            _servoStatistic[i][errcode].cntLastDay = cntlast[i];
        }
    }
}

void ServoStatistic::printServoStatistic(int8_t index)
{
    if (index == -1)
    {
        _PRINT("Robot %d servo statistic:\n", _serialNum);
        for (int i = 0; i < JOINT_NUM; i++)
        {
            _PRINT("Joint %d:\n", i);
            for (auto e : _servoStatistic[i]) { _PRINT("\t0x%x:%5d %5d\n", e.first, getAllCnt(i, e.first), getDaliyCnt(i, e.first)); }
        }
    }
    else
    {
        for (auto e : _servoStatistic[index]) { _PRINT("Servo%d stat:0x%x:%5d %5d\n", index, e.first, getAllCnt(index, e.first), getDaliyCnt(index, e.first)); }
    }
}
int ServoStatistic::check(bool report)
{
    int ret = 0;
    for (int i = 0; i < JOINT_NUM; i++)
    {
        uint32_t jointAllCnt = 0;
        for (auto e : _servoStatistic[i]) { jointAllCnt += getDaliyCnt(i, e.first); }
        if (jointAllCnt > getThreshold(i))
        {
            if (report || !(_reportedDaliyWarn & (1 << i)))
                ret |= 1 << i;
            _reportedDaliyWarn |= 1 << i;
        }
    }
    return ret;
}
std::string ServoStatistic::formatDaliyWarnStr()
{
    std::string ret;
    for (int i = 0; i < JOINT_NUM; i++)
    {
        if (_reportedDaliyWarn & (1 << i))
            ret += std::to_string(i + 1) + " ";
    }
    return ret;
}

void writeServoStatistic() { ServoStatistic::get()->write2File(nullptr); }

/**
 * @brief 
 * 
 * @param jointIndex 
 */
void updateServoStatistic(uint8_t jointIndex) {}